package com.apps.java.javanote02;

import java.io.*;

public class _23IO流_字符流 {
    /**
     * 字符流
     *
     * 	一、概述：由于字节流操作中文不是特别方便，所以java就提供字符流
     * 		字符流 = 字节流 + 编码集
     *
     * 		用字节流复制文本文件时，文本文件也会有中文，但是没有问题，原因是最终底层操作会自动进行字节拼接成中文；
     * 		如何识别是中文呢？
     * 			汉子在存储的时候，无论选择哪种编码存储，第一个字节都是负数
     *
     * 	二、编码表
     * 		1.基础知识：
     * 			（1）计算机中存储的信息都是用二进制数表示的；我们在屏幕上看到的英文、中文等字符是二进制转换之后的结果。
     * 			（2）按照某种规则，将字符存储到计算机中，称为“编码”。反之，将存储在计算机中的二进制数按照某种规则解析显示，
     * 			  	称为“解码”。强调：按照A编码存储，必须按照A编码解析，这样才能显示正确的文本符号，否则会导致乱码现象。
     * 			（3）字符编码：就是一套自然语言的字符和二进制之间的对应规则
     *
     * 		2.字符集：
     * 			（1）是一个系统支持的所有字符的集合，包括各国家文字、标点符号、图形符号、数字等
     * 			（2）计算机要准确的存储和识别各种字符集符号，就需要进行字符编码，一套字符集必然至少优一塔字符编码。
     * 				常见字符集优ASCII字符集、GBXXX字符集、Unicode字符集等
     * 				a.ASCII（American Standard Code for Information Interchange，美国信息交换标准代码）：
     * 					.是基于拉丁字母的一套电脑编码系统，用于显示现代英语，主要包括控制字符（回车键、退格、换行键等）和可显示字符（英文
     * 					   大小写字符、阿拉伯数字和西文符号）；
     * 					.基本的ASCII字符集，使用7位表示一个字符，共128字符。ASCII的扩展字符集使用8位表示一个字符，共256字符，方便
     * 					   支持欧洲常用字符。是一个系统支持的所有字符的集合，包括各国家文字、标点符号、圆形符号、数字等。
     * 				b.GBXXX字符集：
     * 					.GB2312：简体中文码表。一个小于127的字符的意义与原来相同，但两个大于127的字符连在一起时，就表示一个汉子，这样
     * 					   大约可以组合了包含7000多个简体汉字，此外数字符号、罗马希腊的字母、日文的假名等都编进去了，链ASCII里本就有的数
     * 					   字、标点、字母都统统重新编了两个字节的编码，这就是常说的“全角”字符，而原来在127号以下的哪些就交“半角”字符了。
     * 					.GBK：最常用的中文码表。是在GB2312标准基础上的扩展规范，使用了双字节编码方案，共收录了21003个汉字，完全兼容
     * 					 GB2312标准，同时支持繁体汉字以及日韩汉字等。
     * 					.GB18030：最新的中文码表。收录汉字70244个，采用多字节编码，每个字可以由1个、2个或4个字节组成。支持中国国内少
     * 					   数民族的文字，同时支持繁体汉字以及日韩汉字等。
     * 				c.Unicode字符集：
     * 					.为表达任意语言的任意字符而设计，是业界的一种标准，也称为统一码、标准万国码。它最多使用4个字节的数字表达每个字母、符
     * 					   号，或者文字。有三种编码防范，UTF-8、UTF-16和UTF32。最为常用的是UTF-8编码。
     * 					.UTF-8：可以用来表示Unicode标准中任意字符，它是电子邮件、网页及其他存储或传送文字的应用中，优先采用的编码。互联
     * 					   网工程工作小组（IETF）要求所有互联网协议都必须支持UTF-8编码。它使用一至四个字节为每个字符编码。
     * 						编码规则：	128个US-ASCII字符，只需一个字节编码
     * 								拉丁文等字符，需要两个字节编码
     * 								大部分常用字（含中文），使用三个字节编码
     * 								其他极少使用的Unicode辅助字符，使用四字节编码
     * 	三、字符串中的编码解码问题
     * 		编码：
     * 		.byte[] getBytes()：使用平台的默认字符集将该String编码为一系列字节，将结果存储到新的字节数值中
     * 		.byte[] getBytes(String charsetName)：使用指定的字符集将该String编码为一系列字节，将结果存储到新的字节数组中。
     * 		解码：
     * 		.String(byte[] bytes)：通过使用平台的默认字符集解码指定的字节数组来构造新的String
     * 		.String(byte[] bytes,String charsetName)：通过指定的字符集解码指定的字节数组来构造新的String。
     *
     * 	四、字符流中的编码解码问题
     * 		字符流抽象基类：
     * 		.Reader：字符输入流的抽象类
     * 		.Writer：字符输出流的抽象类
     * 		字符流中和编码解码问题相关的两个类：
     * 		.InputStreamReader：是从字节流到字符流的桥梁
     * 			它读取字节，并使用指定的编码将其解码为字符；
     * 			它使用的字符集可以由名称指定，也可以被明确指定，或者可以接收平台的默认字符集。
     * 			子类：FileReader
     * 		.OutputStreamWriter：是从字符流到字节流的桥梁
     * 			使用指定的编码将写入的字符编码转换为字节
     * 			它使用的字符集可以由名称指定，也可以被明确指定，或者可以接受平台的默认字符集；
     * 			子类：FileWrite
     *
     * 	五、字符流写数据的5种方式
     * 		方法名
     * 		void write(int c)							写一个字符
     * 		void write(char[] cbuf)						写入一个字符数组
     * 		void write(char[] cbuf,int off,int len)		写入字符数组的一部分
     * 		void write(String str)						写入一个字符串
     * 		void write(Stringg str,int off,int len)		写入一个字符串的一部分
     *
     * 		方法名
     * 		flush()			刷新流，还可以继续写数据
     * 		close()			关闭流，释放资源，但是在关闭执勤会先刷新流。一旦关闭，就不能再写数据
     *
     * 	六、字符流读数据的2中方式
     * 		方法名
     * 		int read()				一次读一个字符数据
     * 		int read(char[] cbuf)	一次读一个字符数组数据
     *
     * 	七、转换流的子类
     * 		转换流的名字比较长，而我们常见的操作都是按照本地默认编码实现的，所以，为了简化书写，转换流提供了对应的子类
     * 		FileReader：用于读取字符文件的便捷类
     * 			FileReader(String fileName)
     * 		FileWrite：用于写入字符文件的便捷类
     * 			FileWrite(String fileName)
     *
     * 	八、字符缓冲流
     * 		BufferedWrite(Write out)：	将文本写入字符输出流，缓冲字符，以提供单个字符，数组和字符串的高效写入，可以指定缓冲区大小，
     * 									或者可以接收默认大小，默认值足够大，可用于大多数用途
     * 		BufferedReader(Reader in)：	从字符输入流读取文本，缓冲字符，以提供字符，数组和行的高效读取，可以指定缓冲区大小。
     * 									默认值足够大，可用于大多数用途。
     *
     * 		一、字符缓冲流特有功能
     * 			BufferedWrite：
     * 				void newLine()：写一行行分隔符，行分隔符字符串由提供属性定义
     * 			BufferedReader：
     * 				public String readLine()：读一行文字。结果包含行的内容的字符串，不包括任何行终止字符，如果流的结尾已经到达，则为null。
     *
     * 	九、复制文件的异常处理
     * 		1.抛出异常并不能解决异常问题，我们要使用try...catch...finally来处理异常
     * 		2.格式：
     * 			try{
     * 				可能出现异常的代码
     * 			}catch(异常类名  变量名){
     * 				异常的处理代码;
     * 			}finally{
     * 				执行所有清除操作;
     * 			}
     * 		3.JDK7的改进方案：
     * 			try(定义流对象){
     * 				可能出现异常的代码;
     * 			}catch(异常类名 变量名){
     * 				异常的处理代码;
     * 			}//自动释放资源
     * 		4.JDK9之后的改进方案：
     * 			定义输入流对象;
     * 			定义输出流对象;
     * 			try(输入流对象;输出流对象){
     * 				可能出现异常的代码;
     * 			}catch(异常类名 变量名){
     * 				异常的处理代码;
     * 			}//自动释放资源
     *
     *
     *
     */

    public static void main(String[] args) throws Exception {
	/*字符串中的编码解码问题
		//定义一个字符串
		String s = "你好！世界";

		//byte[] getBytes()：使用平台的默认字符集将该String编码为一系列字节，将结果存储到新的字节数值中
		byte[] bytes = s.getBytes();//[-60, -29, -70, -61, -93, -95, -54, -64, -67, -25]

		//byte[] getBytes(String charsetName)：使用指定的字符集将该String编码为一系列字节，将结果存储到新的字节数组中。
		//byte[] bytes = s.getBytes("GBK");//[-60, -29, -70, -61, -93, -95, -54, -64, -67, -25]
		//byte[] bytes = s.getBytes("UTF-8");//[-28, -67, -96, -27, -91, -67, -17, -68, -127, -28, -72, -106, -25, -107, -116]
		System.out.println(Arrays.toString(bytes));

		//String(byte[] bytes)：通过使用平台的默认字符集解码指定的字节数组来构造新的String
		//String ss = new String(bytes);
		//System.out.println(ss);

		//String(byte[] bytes,String charsetName)：通过指定的字符集解码指定的字节数组来构造新的String。
		String ss = new String(bytes,"UTF-8");
		System.out.println(ss);//????????

		*/

        //字符流中的编码解码问题
        //OutputStreamWriter：是从字符流到字节流的桥梁
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("p.txt"),"GBK");
        osw.write("中国");
        osw.close();

        //InputStreamReader：是从字节流到字符流的桥梁
        InputStreamReader isr = new InputStreamReader(new FileInputStream("p.txt"));

        int by;
        while((by = isr.read()) != -1){
            //System.out.print((char)by);
        }

        isr.close();

        //字符流写数据的5种方式
        //创建一个字符输出流
        OutputStreamWriter ow = new OutputStreamWriter(new FileOutputStream("x.txt"),"UTF-8");

        //void write(int c)：写一个字符
        ow.write(97);//使用字符流。如果我们不关闭资源或者刷新流等操作，写入的数据就还会在内存中，不会写入到文件中
        //刷新
        ow.flush();

        //void write(char[] cbuf)：写入一个字符数组
        char[] cha = {45,28,69,66,97,98,99};
        ow.write(cha);
        ow.flush();

        //void write(char[] cbuf,int off,int len)：写入字符数组的一部分
        ow.write(cha, 0, 3);

        //void write(String str)：写入一个字符串
        ow.write("\r\n苏打水Oingting");

        //void write(String str,int off,int len):写入一个字符串的一部分
        ow.write("\r\n我爱我家：我要把这玩意染成绿的", 0, 6);

        //关闭资源
        ow.close();

        //字符流读数据的2种方式
        //创建字符输入流对象
        InputStreamReader ips = new InputStreamReader(new FileInputStream("x.txt"),"UTF-8");

        //int read()：一次读一个字符数据
        int ch;
        while((ch = ips.read()) != -1){
            System.out.print((char)ch);
        }

        //int read(char[] cbuf)	一次读一个字符数组数据
        char[] chs = new char[1024];
        int len;
        while((len = ips.read(chs)) != -1){
            System.out.print(new String(chs,0,len));
        }

        //关闭资源
        ips.close();


        //字符缓冲流
        //BufferedWrite(Write out)
        BufferedWriter bw = new BufferedWriter(new FileWriter("x.txt"));
        bw.write("字符缓冲流！冲冲冲");
        //关闭资源
        bw.close();

        //BufferedReader(Reader in)
        BufferedReader br = new BufferedReader(new FileReader("x.txt"));

		/*
		//一次读取一个字符数据
		int brch;
		while((brch = br.read()) != -1){
			System.out.print((char)brch);
		}
		*/

        //一次读取一个字符数组数据
        char[] brchs = new char[1024];
        int lens;
        while((lens = br.read(brchs)) != -1){
            System.out.print(new String(brchs,0,lens));
        }

        //字符缓冲流的特有功能

        BufferedWriter bwt = new BufferedWriter(new FileWriter("C.java"));

        for(int i=0 ; i<10 ; i++){
            bwt.write("hello" + i);
            //bwt.write("\r\n");
            //void newLine()：写一行行分隔符，行分隔符字符串由提供属性定义
            bwt.newLine();
            bwt.flush();
        }
        bwt.close();


        BufferedReader brd = new BufferedReader(new FileReader("C.java"));

        //public String readLine()：读一行文字。结果包含行的内容的字符串，不包括任何行终止字符，如果流的结尾已经到达，则为null。
		/*
		//第一次读数据
		String readLine = brd.readLine();
		System.out.println(readLine);

		//第二次读数据
		String readLine2 = brd.readLine();
		System.out.println(readLine2);

		//多读一次
		String readLine3 = brd.readLine();
		System.out.println(readLine3);
		*/
        String num;
        while((num = brd.readLine()) != null){
            System.out.print(num);//读取数据时，不会读取换行符。需要手动写换行符
        }

        brd.close();

    }

    //复制文件的异常处理
    //JDK9的改进方法
    private static void method4() throws IOException {
		/*
		FileReader fr = new FileReader("o.txt");
		FileWriter fw = new FileWriter("fw.txt");
		try(fr,fw){

			char[] chs = new char[1024];
			int len;
			while((len = fr.read(chs)) != -1){
				fw.write(chs, 0, len);
			}

		} catch (IOException e) {
			e.printStackTrace();
		}
		*/
    }

    //JDK7的改进版方法
    private static void method3(){//try..catch的改进方案
        try(
                FileReader fr = new FileReader("o.txt");
                FileWriter fw = new FileWriter("fw.txt");
        ){

            char[] chs = new char[1024];
            int len;
            while((len = fr.read(chs)) != -1){
                fw.write(chs, 0, len);
            }

        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } //自动释放资源
    }
    //try..catch..finally
    private static void method2(){//我们应该使用try-catch-finally进行异常处理
        //定义字符输入流和字符输出流
        FileReader fr = null;
        FileWriter fw = null;

        try {
            //创建输入输出流对象
            fr = new FileReader("o.txt");
            fw = new FileWriter("fw.txt");

            //创建字符数组存储文件数据
            char[] chs = new char[1024];
            //定义变量存储文件长度
            int len;
            //循环读取数据并写入数据
            while((len = fr.read(chs)) != -1){
                fw.write(chs, 0, len);
            }

        } catch (IOException e) {
            e.printStackTrace();
        }finally{
            //关闭资源
            //判断fr是否为空，不为空就关闭资源
            if(fr != null){
                try {
                    fr.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(fw != null){
                try {
                    fw.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    //抛出异常
    private static void method1() throws IOException{//抛出异常处理并不是真的的处理异常
        FileReader fr = new FileReader("o.txt");
        FileWriter fw = new FileWriter("fw.txt");

        char[] chs = new char[1024];
        int len;
        while((len = fr.read(chs)) != -1){
            fw.write(chs, 0, len);
        }

        fr.close();
        fw.close();
    }



}
